//
// Created by gj on 16-2-20.
//

#include "Thread.h"


bool ThreadAttr::isDetach() const {
    int detachState = PTHREAD_CREATE_JOINABLE;
    int errCode = pthread_attr_getdetachstate(&p_attr, &detachState);
    if (errCode != 0) {
        LOG_W("--> Call pthread_attr_getdetachstate error: errCode = %d", errCode);
    }
    return detachState == PTHREAD_CREATE_DETACHED;
}

class THREAD_HOLDER {
public:
    THREAD_HOLDER(AsyncRunnable &run) //
            : run(run) {
    }

private:
    AsyncRunnable run;

    friend void *_thread_run(void *);
};

void *_thread_run(void *arg) {
    AsyncRunnable run = ((THREAD_HOLDER *) arg)->run;
    delete (THREAD_HOLDER *) arg;
    return (*run)();
}

void CThread::__create_pthread(ThreadAttr *attr) {
    pthread_attr_t *p_attr = NULL;
    if (attr != NULL) {
        p_attr = &attr->p_attr;
    }
    isRun = attr == NULL || (!attr->isDetach());
    int errCode = pthread_create(&p_id, p_attr, _thread_run, new THREAD_HOLDER(run));
    if (errCode != 0) {
        LOG_E("--> __create_pthread error: errCode = %d", errCode);
#ifdef FLAG_DEBUG
        abort();
#endif
    }
}

CThread::~CThread() {
    if (!joinable()) {
        LOG_E_FUNC("Thread is not join or detach");
#ifdef FLAG_DEBUG
        abort();
#endif
    }
}

#ifdef FLAG_UNIT_TESTING

class TestRunnable : public CRunnable {

public:
    TestRunnable() :LOG_INIT_NAME_DEBUG("TestRunnable")
            CRunnable() {
        bzero(_t, sizeof(_t));
    }

    virtual ~TestRunnable() {
        LOG_I_FUNC("--> start")
    }

    virtual time_t getRunTime() const {
        return 0;
    }

protected:
    LOG_CREATE_DEBUG

    char _t[1 * 1024 * 1024];

    virtual void *run() {
        LOG_I("--> start run")
    }
};

void test_thread() {
    LOG_CREATE_FUNC("test_thread")
    AsyncRunnable asyncRunnable(new TestRunnable());
    Thread thread[10];
    LOG_I("Start test thread join...")
    for (int i = 0; i < 10; ++i) {
        LOG_I("Create thread %d", i)
        thread[i] = new CThread(asyncRunnable);
        LOG_I(LOG_SEPARATOR)
    }
    for (int i = 0; i < 10; ++i) {
        LOG_I("Join thread %d", i)
        thread[i]->join();
        LOG_I(LOG_SEPARATOR)
    }
    LOG_I("Start test thread detach...")
    for (int i = 0; i < 10; ++i) {
        LOG_I("Create detach thread %d", i)
        CThread _thread(asyncRunnable);
        _thread.detach();
        LOG_I(LOG_SEPARATOR)
    }
    LOG_I("Test end...")
}

void test_thread_time() {
    LOG_CREATE_FUNC("test_thread_time")
    const int num = 100;
    AsyncRunnable asyncRunnable(new TestRunnable());
    Thread thread[num];
    timeval _val;
    gettimeofday(&_val, NULL);
    int64_t _time = _val.tv_sec * 1000000 + _val.tv_usec;
    LOG_I("--> Start %ld", _time)
    for (int i = 0; i < num; ++i) {
        thread[i] = new CThread(asyncRunnable);
    }
    for (int i = 0; i < num; ++i) {
        thread[i]->join();
    }
    gettimeofday(&_val, NULL);
    LOG_I("--> End %ld", _val.tv_sec * 1000000 + _val.tv_usec - _time);
}

void test_thread_super_detach() {
    LOG_CREATE_FUNC("test_thread_super_detach")
    const int num = 100;
    AsyncRunnable asyncRunnable(new TestRunnable());
    timeval _val;
    gettimeofday(&_val, NULL);
    int64_t _time = _val.tv_sec * 1000000 + _val.tv_usec;
    LOG_I("--> Start %ld", _time)
    for (int i = 0; i < num; ++i) {
        CThread thread(asyncRunnable);
        thread.detach();
    }
    gettimeofday(&_val, NULL);
    LOG_I("--> End %ld", _val.tv_sec * 1000000 + _val.tv_usec - _time);
}

void thread_unit_testing() {
    for (int i = 0; i < 100; ++i) {
        test_thread();
        test_thread_time();
        for (int j = 0; j < 10; ++j) {
            test_thread_super_detach();
        }
    }
}

#endif